# -*- coding: utf-8 -*- 
import wx
import socket, sys, threading,time
from pyDemin import *
verrou=threading.Lock()
class ClientKernel:
    def __init__(self):
		
	#Les threads
	self.th_R=-1
	self.th_E=-1
	
       
	#Les paramètres du joueur
	self.nom="ff"
	
	#Les paramètres de connexion
	self.port=8003
	self.ip="127.0.0.1"
	
	#Les paramètres du démineur
	self.lon=-1
	self.lar=-1
	self.nm=-1
	self.grille=numpy.zeros((1,1))
        self.grille[0,0]=-10
	
	#Les paramètres de partie
	self.nbj=-1
	self.maxj=-1
        self.infos=[]
        self.infosunjourrecues=False
    def set_param_connexions(self,port,ip):
	self.port=port
	self.ip=ip

    def set_nom(self,nom):
	self.nom=nom
	
    def connect(self,win):
	#si retourne 1 la connexion a échouée
	#si retroune 2 la connexion existe déjà...
	#si retourne 0 all is fine !
	connexion = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	try:
		connexion.connect((self.host,self.port))
#		print "Connexion etablie avec le serveur."
		if self.th_R == -1 and self.th_E == -1:
			self.th_R = ThreadReception(win,connexion)
			self.th_E = ThreadEmission(win,connexion)
			self.th_E.start()
			self.th_R.start()
		else:
			return 2
	except socket.error:
#		print "Connexion failed"
		return 1
	return 0

    def getInfos(self,msg_recu):
        verrou.acquire()
        if not self.infosunjourrecues:
            for j in range(int(msg_recu[1])):
                self.infos.append([])
            self.infosunjourrecues=True
        for i in range(int(msg_recu[1])):
#            print "boucle"
            self.infos[i]=[msg_recu[i*3+2],msg_recu[i*3+3],msg_recu[i*3+4]]
#        print self.infos
        verrou.release()

    def clickG(self,x,y):
 #       print "envoi ClickG"+str(x)+str(y)
	self.th_E.envoiclikG(x,y)

    def clickD(self,x,y):
	self.th_E.envoiclikD(x,y)

    def disconnect(self):
 #       print "disconnect"
        verrou.acquire()
	self.th_E.envoiFin()
	self.th_E._Thread__stop()
	self.th_R._Thread__stop()
	self.th_E=-1
	self.th_R=-1
        verrou.release()

    def set_nbj(self,nbj):
#        verrou.acquire()
	self.nbj=nbj
#        verrou.release()
    def set_maxj(self,maxj):
        verrou.acquire()
	self.maxj=maxj
        verrou.release()

    def get_nbj(self):
        verrou.acquire()
        verrou.release()
	return self.nbj
    

    def get_maxj(self):
        
	return self.maxj

    def get_nom(self):
        return self.nom

    def get_ip(self):
        return self.ip

    def get_port(self):
        return self.port

    def testparam(self,nom,ip,port):
        #On teste la validité des chaines envoyées en paramètres !
	tx1_text=nom
	test1=False
#	print len(tx1_text)
	if len(tx1_text)>0:
       #le nom doit juste être différent d'une chaine vide
		test1=True
	tx2_text=ip
	test2=False
	tx2_splitted=tx2_text.split(".")
	if len(tx2_splitted)==4:
       #l'adresse doit être comp. de 4 nb sép. par des pts
		try:
			for i in tx2_splitted: 
                        #hack pour test de la numéricité
				int(i)
				test2=True
		except:
			test2=False
	tx3_text=port
	test3=False
#	print(tx3_text)
	try: 
              #hack pour tester la numéricité
		int(tx3_text)
		test3=True
	except:
		test3=False
	erreur=""
	if not test1:
		erreur+=" Le nom d'utilisateur n'est pas rempli ! "
	else:
		self.nom = tx1_text
	if not test2:
		erreur+=" L'adresse est dans un format incorrect ! "
	else:
		self.host = tx2_text
	if not test3:
		erreur+=" Le port n'est pas dans un format correct ! "
	else:
		self.port = int(tx3_text)
	if erreur <> "":
		return erreur
	else:
		return True
		
    def set_param_demin(self,lon,lar,nm):
        verrou.acquire()
#        print "in set_parma_demin"
	self.lon=lon
	self.lar=lar
	self.nm=nm
	self.grille=numpy.zeros((self.lon,self.lar))
        for i in range(len(self.grille)):
            for j in range(len(self.grille[i])):
                self.grille[i,j]=-1
#        print self.grille
        verrou.release()
    def update_grille(self,msg_recu):
        verrou.acquire()
#	print "****Grille Avant****"
#        print self.grille
        for i in range(self.lar):#on reconstruit la grille a partir de la nouvelle grille recue
            for j in range(self.lon):
                self.grille[i,j]=int(msg_recu[i*self.lar+j+1])
 #       print "****Grille après****"
 #       print self.grille
        verrou.release()
        return 0


    def set_nom(self,nom):
        self.nom=nom

    def set_ip(self,ip):
        self.ip=ip
    
    def set_port(self,port):
        self.port=port

    def envoiOK(self):
        self.th_E.envoiOK()

class PanInfApp(wx.App):
    def OnInit(self):
        win = PanInfFrame(None,1,"Ma fenêtre",size=(600,600))
        win.Show(True)
        self.SetTopWindow(win)
        return True

class PanInfFrame(wx.Frame):
    def __init__(self,parent,ID,title,pos=wx.DefaultPosition,size=wx.DefaultSize,style=wx.DEFAULT_FRAME_STYLE):
        wx.Frame.__init__(self,parent,ID,title,pos,size,style)
        self.kernel=ClientKernel()
        self.panel=PanInformations(self,11)
        self.panel.init_buffer()
        self.CreateStatusBar()
        self.SetStatusText("")
    def FinParam(self):
        self.panel.Destroy()
        self.StatusBar.Destroy()
#        print "panel Infos Détruit!"
        self.panel=PanDemineur(self,12)
class PanDemineur(wx.Panel):
    def __init__(self,parent,ID):
        wx.Panel.__init__(self,parent,ID)
        self.parent=parent
        self.SetSize(parent.GetClientSize())
        self.buffer=0
        self.loncase=0
        self.larcase=0
        self.state=0
        self.marge=100
        verrou=threading.Lock()

        self.init_buffer()
        self.Bind(wx.EVT_PAINT,self.OnPaint)
        self.Bind(wx.EVT_LEFT_DOWN,self.OnLeft)
        self.Bind(wx.EVT_RIGHT_DOWN,self.OnRight)
#        print 'dessine'
    def init_buffer(self):
        taille=self.parent.GetClientSize()
        w,h=self.GetClientSize()
        self.buffer = wx.EmptyBitmap(w,h)
        dc = wx.BufferedDC(wx.ClientDC(self),self.buffer)
        dc.SetBrush(wx.Brush("white"))
        dc.SetPen(wx.Pen("white",1))
        dc.DrawRectangle(0,0,w,h)
     
        if self.state<>0:
#            print "init buffer"
            self.dessineDemin(dc)
            self.printInfos(dc)
    def reinit_buffer(self,gagne=False,perdu=False):
       
 #       print 'reinit_buffer'
        dc = wx.BufferedDC(wx.ClientDC(self),self.buffer)
        dc.Clear()

        self.dessineDemin(dc)
        self.printInfos(dc)
        if gagne:
            self.draw3(dc)
        if perdu:
            self.draw2(dc)
        
    def OnLeft(self,evt):
        x,y = evt.GetPosition()
        i=int(x/self.loncase)
        j=int(y/self.larcase)
		#ici traduction de x,y en case !
        self.parent.kernel.clickG(i,j)

    def OnRight(self,evt):
        x,y = evt.GetPosition()
        i=int(x/self.loncase)
        j=int(y/self.larcase)
		#ici traduction de x,y en case !
        self.parent.kernel.clickD(i,j)
        
    def printInfos(self,dc):
        verrou.acquire()
 #       print 'print_infos'
        

       
        taille=self.parent.GetClientSize()
        LargByjoueur=(taille[0]-10)/self.parent.kernel.get_maxj()
        dc.SetBrush(wx.Brush("white"))
        dc.SetPen(wx.Pen("red",1))
	dc.DrawRectangle(5,taille[1]-self.marge+5,taille[0]-5,self.marge-10)
#        print self.parent.kernel.get_maxj()
        for i in range(len(self.parent.kernel.infos)):
            dc.SetPen(wx.Pen("blue",1))
            coingauche=(5+i*LargByjoueur,taille[1]-self.marge+5)
            tailletruc=(LargByjoueur,self.marge-10)
            dc.DrawRectangle(coingauche[0],coingauche[1],tailletruc[0],tailletruc[1])
            dc.DrawText("Joueur : "+str(self.parent.kernel.infos[i][0]),coingauche[0]+10,int(coingauche[1]+self.marge*1./5))
            etat=""
            if int(self.parent.kernel.infos[i][1])==0:
                etat="En jeu !"
            elif int(self.parent.kernel.infos[i][1])==1:
                etat="Le joueur a perdu"
            else:
                etat="Le joueur a gagne"
            dc.DrawText(etat,coingauche[0]+10,int(coingauche[1]+self.marge*2./5))
            dc.DrawText(str(self.parent.kernel.infos[i][2])+" cases dévoilées !",coingauche[0]+10,int(coingauche[1]+self.marge*3./5))
        verrou.release()
    def dessineDemin(self,dc):
        verrou.acquire()
#        print"dessineDemin"
        taille=self.parent.GetClientSize()
        l=self.parent.kernel.lar
        L=self.parent.kernel.lon
        loncase=min(taille[1]/l,(taille[0]-self.marge)/L)
        larcase=loncase
        self.larcase=larcase
        self.loncase=loncase
     
        if self.parent.kernel.grille[0,0]<>-10:
            grille=self.parent.kernel.grille
     
#        print grille
    	
       
        dc.SetBrush(wx.Brush("red"))
        dc.SetPen(wx.Pen("black",3))
        k=0
        for i in range(l):
			for j in range(L):
				if grille[i,j]==-1:
					dc.SetBrush(wx.Brush("grey"))
					dc.DrawRectangle(i*larcase,j*loncase,larcase,loncase)
                          
				elif  grille[i,j]!=0 and grille[i,j]!=-2:
					dc.DrawText("%d"%grille[i,j],i*larcase+11,j*loncase+7)
				elif grille[i,j]==-2:
					dc.SetBrush(wx.Brush("grey"))
					dc.DrawRectangle(i*larcase,j*loncase,larcase,loncase)
					dc.SetBrush(wx.Brush("red"))
					dc.DrawLine(i*larcase+10,j*loncase+20,i*larcase+10,j*loncase+5)
					dc.DrawRectangle(i*larcase+10,j*loncase+5,10,7)
					dc.DrawLine(i*larcase+8,j*loncase+20,i*larcase+13,j*loncase+20)
					k+=1
        verrou.release()

    def draw2(self,dc):
                verrou.acquire()
		dc.Clear()
		dc.SetBrush(wx.Brush("black"))
		dc.SetPen(wx.Pen("black",1))
		
		dc.SetBrush(wx.Brush("yellow"))
		dc.SetPen(wx.Pen("black",3))
		dc.DrawCircle(300-self.marge/2,300-self.marge/2,200-self.marge)
		dc.SetBrush(wx.Brush("blue"))
		dc.DrawLine(220-self.marge/2,230-self.marge/2,220-self.marge/2,270-self.marge/2)
		dc.DrawLine(200-self.marge/2,250-self.marge/2,240-self.marge/2,250-self.marge/2)
		dc.DrawLine(360-self.marge/2,250-self.marge/2,400-self.marge/2,250-self.marge/2)
		dc.DrawLine(380-self.marge/2,230-self.marge/2,380-self.marge/2,270-self.marge/2)
		dc.SetBrush(wx.Brush("red"))
		dc.DrawLine(150-self.marge/2,400-self.marge/2,450-self.marge/2,400-self.marge/2)
                verrou.release()
    def draw3(self,dc):
                verrou.acquire()
		dc.Clear()
		dc.SetBrush(wx.Brush("yellow"))
		dc.SetPen(wx.Pen("black",3))
		dc.DrawCircle(300,300,200)
		dc.SetBrush(wx.Brush("blue"))
		dc.DrawCircle(220,250,20)
		dc.DrawCircle(380,250,20)
		dc.SetBrush(wx.Brush("yellow"))
		dc.DrawArc(150,300,450,300,300,300)
		dc.SetPen(wx.Pen("yellow",3))
		dc.DrawLine(150,300,450,300)
                verrou.release()

    def OnPaint(self,evt):
        dc = wx.BufferedPaintDC(self,self.buffer)
        self.reinit_buffer()
class PanInformations(wx.Panel):
    def __init__(self,parent,ID):
        wx.Panel.__init__(self,parent,ID)
        self.SetSize(parent.GetClientSize())
        self.parent=parent
        verrou=threading.Lock()
        self.tx00=wx.StaticText(self,111,"Player Name",(60,00))
        self.tx1=wx.TextCtrl(self,112,self.parent.kernel.get_nom(),(60,20),size=(340,20),style=wx.TE_PROCESS_ENTER)
       
        self.tx01=wx.StaticText(self,113,"host",(60,40))
        self.tx2=wx.TextCtrl(self,114,self.parent.kernel.get_ip(),(60,60),size=(340,20),style=wx.TE_PROCESS_ENTER) 
        #style-> enter pas considere comme un caractere

        self.tx02=wx.StaticText(self,115,"port",(60,80))
        self.tx3=wx.TextCtrl(self,116,str(self.parent.kernel.get_port()),(60,100),size=(340,20),style=wx.TE_PROCESS_ENTER)

        self.st1=wx.Button(self,117,"Start Research",(100,140),(120,40))
        self.st2=wx.Button(self,119,"Modify",(100,140),(120,40))
        self.st2.Show(False)
        self.Bind(wx.EVT_BUTTON,self.go,id=117)
        self.Bind(wx.EVT_BUTTON,self.modifyControlesConnexion,id=119)
        self.stt=wx.Button(self,118,"Start",(100,220),(120,40))
	self.Bind(wx.EVT_BUTTON,self.start,id=118)
        self.stt.Show(False)

        self.st11=wx.StaticText(self,119,self.parent.kernel.get_nom(),(60,20))
	self.st12=wx.StaticText(self,1110,self.parent.kernel.get_ip(),(60,60))
        self.st13=wx.StaticText(self,1111,str(self.parent.kernel.get_port()),(60,100))
        self.st11.Show(False)
        self.st12.Show(False)
        self.st13.Show(False)
    def init_buffer(self):
        w,h = self.GetClientSize()
        self.buffer = wx.EmptyBitmap(w,h)
        dc = wx.BufferedDC(wx.ClientDC(self),self.buffer)
 
    def TimeToQuit(self,event):
        self.parent.kernel.disconnect()
        self.parent.Close(True)

    def updateNBJ(self):
        print "update NBJ"

       print("There are "+str(self.parent.kernel.nbj)+" players online. "+str(int(self.parent.kernel.maxj)-int(self.parent.kernel.nbj))+" waiting !")
    
    def bloqueControlesConnexion(self):
        self.tx1.Show(False)
        self.tx2.Show(False)
        self.tx3.Show(False)
        self.st11.Show(True)
        self.st11.SetLabel(self.parent.kernel.get_nom())
        self.st12.Show(True)
        self.st12.SetLabel(self.parent.kernel.get_ip())
        self.st13.Show(True)
        self.st13.SetLabel(str(self.parent.kernel.get_port()))
        self.st1.Show(False)
        self.st2.Show(True)
      

    def libereControlesConnexion(self):
        self.tx1.Show(True)
        self.tx2.Show(True)
        self.tx3.Show(True)
        self.st11.Show(False)
        self.st12.Show(False)
        self.st13.Show(False)

    def modifyControlesConnexion(self,event):
        verrou.acquire()
        self.parent.kernel.disconnect()
        self.libereControlesConnexion()
        self.parent.SetStatusText("")
        self.parent.kernel.set_nbj(0)
        self.parent.kernel.set_maxj(0)
        self.st1.Show(True)
        self.st2.Show(False)
        verrou.release()

    def go(self,event):#lance la connexion au serveur !
       
        nom = self.tx1.GetString(0,-1)
        ip = self.tx2.GetString(0,-1)
        port = self.tx3.GetString(0,-1)
        val=self.parent.kernel.testparam(nom,ip,port)
        if val==True:
            verrou.acquire()
            self.parent.kernel.set_nom(nom)
            self.parent.kernel.set_ip(ip)
            self.parent.kernel.set_port(int(port))
            val2=self.parent.kernel.connect(self.parent)
            verrou.release()             
            if val2<>0:
                print "Es war ein Problem"
                return 1
            self.bloqueControlesConnexion()
            self.parent.kernel.th_E.needNbjoueurs()
        else:
            self.parent.SetStatusText(val)

    def afficherStart(self):
        self.stt.Show(True)
        
    def start(self,event):
        self.parent.kernel.envoiOK()
        self.parent.FinParam()

   

    def grillerecue(self):
        #lancer la deuxième partie du programme !
        return 1
    


class ThreadReception(threading.Thread): #objet thread gerant la reception des messages
    
    def __init__(self,fenetre,conn):
        threading.Thread.__init__(self)
	self.app=fenetre
	self.connexion= conn
#       print "inited"
    def run(self):
        while 1:
            msg_recu = self.connexion.recv(1024)
	    msg_recu=msg_recu.split(",")
#	    print msg_recu
	    if msg_recu[0]=="" or msg_recu[0].upper()=="FIN":
                break
	    if msg_recu[0]=="M":
 #               print "*" + msg_recu[1] + "*"
	    if msg_recu[0]=="G":
  #              print "in G"
                lon=int(msg_recu[1])
                lar=int(msg_recu[2])
                nm=int(msg_recu[3])
                self.app.kernel.set_param_demin(lon,lar,nm)
                self.app.panel.state=1
                #self.app.panel.reinit_buffer()

            if msg_recu[0]=="Gupdate":
                self.app.kernel.th_E.infos()
                self.app.kernel.update_grille(msg_recu)
                self.app.panel.reinit_buffer()
	    if msg_recu[-1].split(".")[0]=="nbj":
                #print"recu nbj"
                self.app.kernel.set_nbj(int(msg_recu[-1].split(".")[1]))
                self.app.kernel.set_maxj(int(msg_recu[-1].split(".")[2]))
                self.app.panel.updateNBJ()
                if int(self.app.kernel.get_nbj())==int(self.app.kernel.get_maxj()):
                    if int(self.app.kernel.get_nbj())!=0:
                        self.app.panel.afficherStart()
        # Si on est sorti du while, on a recu un arrêt de la réception. Le thread <reception> se termine ici.
        # On force la fermeture du thread <emission> :
            if msg_recu[0]=="inf":
               # print "gere inf recu"
                self.app.kernel.getInfos(msg_recu)
                self.app.panel.reinit_buffer()
            if msg_recu[0]=="GAGNE":
                self.app.panel.reinit_buffer(gagne=True)
            if msg_recu[0]=="PERDU":
                self.app.panel.reinit_buffer(perdu=True)



        if self.app.kernel.th_E<>-1:
		self.app.kernel.th_E._Thread__stop()
        print "Client arrete. Connexion interrompue."
        self.connexion.close()




class ThreadEmission(threading.Thread): #objet thread gerant l'emission des messages
    
    def __init__(self,fenetre,conn):
        threading.Thread.__init__(self)
        self.connexion = conn # ref. du socket de connexion
	self.app = fenetre


    def envoiM(self,a):
        self.connexion.send(a)
    def envoiFin(self):
        self.connexion.send("FIN")
    def envoiclikD(self,x,y):
        self.connexion.send("R, %d ,%d"%(int(x),int(y)))

    def envoiclikG(self,x,y):
        self.connexion.send("L, %d ,%d"%(int(x),int(y)))
    def envoiOK(self):
        print "In envoi OK,"+self.app.kernel.get_nom()
        stri="OK,"+self.app.kernel.get_nom()
        self.connexion.send(stri)
    def needNbjoueurs(self):
        self.connexion.send("nbj,1")
	    
    def infos(self):
        self.connexion.send("info")



app = PanInfApp()
app.MainLoop()
